using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.SharePoint;
using System.Xml;

namespace Community.SharePoint.Tools.SPLookupLinker
{
    /// <summary>
    /// Douglas Leung 2008 November
    /// This tool was developed to relink lookup field types to lists in a site. 
    /// This tool is useful in the following scenarios:
    /// 1. A list a lookup field is pointing to has been deleted and needs to be linked to a different field in a different list
    /// 2. A site backup is made and is restored in a different environment - The lookup fields will no longer link to 
    /// the field even thought the appropriate lists may be created. This tool will help relink the fields
    /// This tool is able to:
    /// 1. Relink lookup fields
    /// 2. Relink multiple lookup fields (Bulk tasks)
    /// 3. View Field Schema
    /// 
    /// Requirements for this tool to run:
    /// 1. Must login as Site Administrator - as the code will delete the lookup field and recreate it
    /// 2. Did not use RunWithElevatedPrivileges() for security purposes.
    /// 3. Tool must be run on a WSS v3.0 or MOSS 2007 Server directly
    /// 4. Sorry, this tool has only been tested on an English environment - but theoretically should still work 
    /// with other localisations of SharePoint instances.
    /// </summary>
    public static class SPOperations
    {
        public static Dictionary<string, SPList> GetLists(string url)
        {
            Dictionary<string, SPList> listCollection = new Dictionary<string, SPList>();
            using (SPSite site = new SPSite(url))
            {
                using (SPWeb web = site.OpenWeb())
                {
                    foreach (SPList list in web.Lists)
                    {
                        try
                        {
                            listCollection.Add(list.Title, list);
                        }
                        catch { }
                    }
                }
            }

            return listCollection;
        }

        public static Dictionary<string, SPField> GetFields(SPList list)
        {
            Dictionary<string, SPField> fieldCollection = new Dictionary<string, SPField>();
            foreach (SPField field in list.Fields)
            {
                try
                {
                    fieldCollection.Add(field.Title, field);
                }
                catch { }
            }
            return fieldCollection;
        }

        public static SPList GetList(string SPWebUrl, string listName)
        {
            SPList returnList = null;
            using (SPSite site = new SPSite(SPWebUrl))
            {
                using (SPWeb web = site.OpenWeb())
                {
                    try
                    {
                        returnList = web.Lists[listName];
                    }
                    catch 
                    {
                        try
                        {
                            returnList = web.GetList(@"/Lists/" + listName.Replace(" ", "%20"));
                        }
                        catch 
                        {
                            returnList = web.Lists[listName];
                        }
                    }
                }
            }

            return returnList;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="SPWebUrl"></param>
        /// <param name="listName"></param>
        /// <param name="fieldName">Field Internal Name</param>
        /// <returns></returns>
        public static SPField GetField(string SPWebUrl, string listName, string fieldName)
        {
            SPList list = null;
            SPField field = null;
            using (SPSite site = new SPSite(SPWebUrl))
            {
                using (SPWeb web = site.OpenWeb())
                {
                    try
                    {
                        list = web.Lists[listName];
                    }
                    catch
                    {
                        list = web.GetList(@"/Lists/"+listName.Replace(" ","%20"));
                    }
                    try
                    {
                        field = list.Fields.GetFieldByInternalName(fieldName);
                    }
                    catch
                    {
                        try
                        {
                            field = list.Fields.GetField(fieldName);
                        }
                        catch
                        {
                            field = list.Fields[fieldName];
                        }
                    }
                }
            }

            return field;
        }

        public static bool RecreateLookupField(SPList list, SPField field, SPList lookupList, SPField lookupListField)
        {
            bool results = false;
            string fieldName = field.Title;
            string lookupListFieldName = lookupListField.Title;
            bool temp = list.ParentWeb.AllowUnsafeUpdates;
            list.ParentWeb.AllowUnsafeUpdates = true;

            //View Guid, Field Guid
            Dictionary<Guid, List<string>> fieldExistenceInViews = new Dictionary<Guid, List<string>>();
            SPFieldLookup lookupField = list.Fields[fieldName] as SPFieldLookup;
            
            if (lookupField != null)
            {
                string newFieldSchema = lookupField.SchemaXml;
                string newFieldTitle = field.Title;
                string oldLookupListId = lookupField.LookupList;
                bool? showInViews = lookupField.ShowInViewForms;

                #region Update Schema Values
                XmlDocument doc = new XmlDocument();
                doc.LoadXml(newFieldSchema);

                XmlNodeList nodesList = doc.GetElementsByTagName("Field");
                foreach (XmlNode node in nodesList)
                {
                    node.Attributes["List"].Value = "{" + lookupList.ID.ToString() + "}";
                    node.Attributes["ID"].Value = "{" + lookupField.Id.ToString() + "}";
                    node.Attributes["SourceID"].Value = "{" + list.ID.ToString() + "}";
                }
                #endregion

                #region Save View Schema
                foreach(SPView view in list.Views)
                {
                    if (view.ViewFields.Exists(lookupField.InternalName))
                    {
                        List<string> viewFields = new List<string>();
                        foreach (string viewField in view.ViewFields)
                        {
                            viewFields.Add(viewField);
                        }
                        fieldExistenceInViews.Add(view.ID, viewFields);
                    }
                }
                #endregion

                list.Fields.Delete(field.InternalName);

                list.Update();

                newFieldSchema = doc.InnerXml;
                string newFieldID = list.Fields.AddFieldAsXml(newFieldSchema);

                list.Update();

                #region Save View Schema
                foreach (KeyValuePair<Guid, List<string>> viewFields in fieldExistenceInViews)
                {
                    SPView view = list.Views[viewFields.Key];
                    view.ViewFields.DeleteAll();
                    foreach (string viewField in viewFields.Value)
                    {
                        view.ViewFields.Add(viewField);
                        view.Update();
                    }
                }
                #endregion

                list.Update();

                list.ParentWeb.AllowUnsafeUpdates = temp;
                results = true;
            }
            return results;
        }

        public static string GetFieldSchema(SPList list, SPField field)
        {
            string results = string.Empty;
            string fieldName = field.Title;

            SPFieldLookup lookupField = list.Fields[fieldName] as SPFieldLookup;
            results = lookupField.SchemaXml;

            return results;
        }
    }
}
